if (!require('ggplot2')) install.packages('ggplot2'); library('ggplot2')
if (!require('plotly')) install.packages('plotly'); library('plotly')
if (!require('tidyverse')) install.packages('tidyverse'); library('tidyverse')
if (!require('treemap')) install.packages('treemap'); library('treemap')
if (!require('scales')) install.packages('scales'); library('scales')
if(!require('Rmisc')) install.packages('Rmisc'); library('Rmisc')
if(!require('lessR')) install.packages('lessR'); library('lessR')
if(!require('xfun')) install.packages('xfun'); library('xfun')
if(!require('corrplot')) install.packages('corrplot'); library('corrplot')
if (!require('factoextra')) install.packages('factoextra'); library('factoextra')
if (!require('GGally')) install.packages('GGally'); library('GGally')
if (!require('dplyr')) install.packages('dplyr'); library('dplyr')
Per la resolució d’aquesta pràctica utilitzarem el dataset Marketing Campaign de Kaggle, aquest conté 2.240 registres sobre l’impacte de les campanyes de marketing sobre cada client, amb la respectiva informació personal i de compres d’aquests. L’objectiu d’aquest conjunt de dades és aconseguir optimitzar les campanyes de marketing de cares al futur amb la missió de maximitzar-ne les conversions.
La forma més òptima de millorar el ratio de conversió de les campanyes seria tenir campanyes personalitzades per cada client, però com que pot ser complexe i molt costos ens haurem de centrar en el que vol la majoria, per tant en aquesta visualització ens centrarem en la següent pregunta:
Quin és el client ideal i quina és la seva campanya de marketing òptima?
Com he explicat al primer apartat, el joc de dades escollit és Marketing Campaign de Kaggle, a continuació el carreguem a la nostre pràctica:
marketing <- read.csv('marketing_campaign.csv', head = TRUE, sep=";")
str(marketing)
## 'data.frame': 2240 obs. of 29 variables:
## $ ID : int 5524 2174 4141 6182 5324 7446 965 6177 4855 5899 ...
## $ Year_Birth : int 1957 1954 1965 1984 1981 1967 1971 1985 1974 1950 ...
## $ Education : chr "Graduation" "Graduation" "Graduation" "Graduation" ...
## $ Marital_Status : chr "Single" "Single" "Together" "Together" ...
## $ Income : int 58138 46344 71613 26646 58293 62513 55635 33454 30351 5648 ...
## $ Kidhome : int 0 1 0 1 1 0 0 1 1 1 ...
## $ Teenhome : int 0 1 0 0 0 1 1 0 0 1 ...
## $ Dt_Customer : chr "2012-09-04" "2014-03-08" "2013-08-21" "2014-02-10" ...
## $ Recency : int 58 38 26 26 94 16 34 32 19 68 ...
## $ MntWines : int 635 11 426 11 173 520 235 76 14 28 ...
## $ MntFruits : int 88 1 49 4 43 42 65 10 0 0 ...
## $ MntMeatProducts : int 546 6 127 20 118 98 164 56 24 6 ...
## $ MntFishProducts : int 172 2 111 10 46 0 50 3 3 1 ...
## $ MntSweetProducts : int 88 1 21 3 27 42 49 1 3 1 ...
## $ MntGoldProds : int 88 6 42 5 15 14 27 23 2 13 ...
## $ NumDealsPurchases : int 3 2 1 2 5 2 4 2 1 1 ...
## $ NumWebPurchases : int 8 1 8 2 5 6 7 4 3 1 ...
## $ NumCatalogPurchases: int 10 1 2 0 3 4 3 0 0 0 ...
## $ NumStorePurchases : int 4 2 10 4 6 10 7 4 2 0 ...
## $ NumWebVisitsMonth : int 7 5 4 6 5 6 6 8 9 20 ...
## $ AcceptedCmp3 : int 0 0 0 0 0 0 0 0 0 1 ...
## $ AcceptedCmp4 : int 0 0 0 0 0 0 0 0 0 0 ...
## $ AcceptedCmp5 : int 0 0 0 0 0 0 0 0 0 0 ...
## $ AcceptedCmp1 : int 0 0 0 0 0 0 0 0 0 0 ...
## $ AcceptedCmp2 : int 0 0 0 0 0 0 0 0 0 0 ...
## $ Complain : int 0 0 0 0 0 0 0 0 0 0 ...
## $ Z_CostContact : int 3 3 3 3 3 3 3 3 3 3 ...
## $ Z_Revenue : int 11 11 11 11 11 11 11 11 11 11 ...
## $ Response : int 1 0 0 0 0 0 0 0 1 0 ...
Veiem com conté un bon volum de registres amb 2.240 observacions i una gran varietat d’informació amb 29 variables, procedim a explicar-les i dividir-les segons categòriques, binàries i numèriques:
A continuació estudiem possibles anomalies:
summary(marketing)
## ID Year_Birth Education Marital_Status
## Min. : 0 Min. :1893 Length:2240 Length:2240
## 1st Qu.: 2828 1st Qu.:1959 Class :character Class :character
## Median : 5458 Median :1970 Mode :character Mode :character
## Mean : 5592 Mean :1969
## 3rd Qu.: 8428 3rd Qu.:1977
## Max. :11191 Max. :1996
##
## Income Kidhome Teenhome Dt_Customer
## Min. : 1730 Min. :0.0000 Min. :0.0000 Length:2240
## 1st Qu.: 35303 1st Qu.:0.0000 1st Qu.:0.0000 Class :character
## Median : 51382 Median :0.0000 Median :0.0000 Mode :character
## Mean : 52247 Mean :0.4442 Mean :0.5062
## 3rd Qu.: 68522 3rd Qu.:1.0000 3rd Qu.:1.0000
## Max. :666666 Max. :2.0000 Max. :2.0000
## NA's :24
## Recency MntWines MntFruits MntMeatProducts
## Min. : 0.00 Min. : 0.00 Min. : 0.0 Min. : 0.0
## 1st Qu.:24.00 1st Qu.: 23.75 1st Qu.: 1.0 1st Qu.: 16.0
## Median :49.00 Median : 173.50 Median : 8.0 Median : 67.0
## Mean :49.11 Mean : 303.94 Mean : 26.3 Mean : 166.9
## 3rd Qu.:74.00 3rd Qu.: 504.25 3rd Qu.: 33.0 3rd Qu.: 232.0
## Max. :99.00 Max. :1493.00 Max. :199.0 Max. :1725.0
##
## MntFishProducts MntSweetProducts MntGoldProds NumDealsPurchases
## Min. : 0.00 Min. : 0.00 Min. : 0.00 Min. : 0.000
## 1st Qu.: 3.00 1st Qu.: 1.00 1st Qu.: 9.00 1st Qu.: 1.000
## Median : 12.00 Median : 8.00 Median : 24.00 Median : 2.000
## Mean : 37.53 Mean : 27.06 Mean : 44.02 Mean : 2.325
## 3rd Qu.: 50.00 3rd Qu.: 33.00 3rd Qu.: 56.00 3rd Qu.: 3.000
## Max. :259.00 Max. :263.00 Max. :362.00 Max. :15.000
##
## NumWebPurchases NumCatalogPurchases NumStorePurchases NumWebVisitsMonth
## Min. : 0.000 Min. : 0.000 Min. : 0.00 Min. : 0.000
## 1st Qu.: 2.000 1st Qu.: 0.000 1st Qu.: 3.00 1st Qu.: 3.000
## Median : 4.000 Median : 2.000 Median : 5.00 Median : 6.000
## Mean : 4.085 Mean : 2.662 Mean : 5.79 Mean : 5.317
## 3rd Qu.: 6.000 3rd Qu.: 4.000 3rd Qu.: 8.00 3rd Qu.: 7.000
## Max. :27.000 Max. :28.000 Max. :13.00 Max. :20.000
##
## AcceptedCmp3 AcceptedCmp4 AcceptedCmp5 AcceptedCmp1
## Min. :0.00000 Min. :0.00000 Min. :0.00000 Min. :0.00000
## 1st Qu.:0.00000 1st Qu.:0.00000 1st Qu.:0.00000 1st Qu.:0.00000
## Median :0.00000 Median :0.00000 Median :0.00000 Median :0.00000
## Mean :0.07277 Mean :0.07455 Mean :0.07277 Mean :0.06429
## 3rd Qu.:0.00000 3rd Qu.:0.00000 3rd Qu.:0.00000 3rd Qu.:0.00000
## Max. :1.00000 Max. :1.00000 Max. :1.00000 Max. :1.00000
##
## AcceptedCmp2 Complain Z_CostContact Z_Revenue
## Min. :0.00000 Min. :0.000000 Min. :3 Min. :11
## 1st Qu.:0.00000 1st Qu.:0.000000 1st Qu.:3 1st Qu.:11
## Median :0.00000 Median :0.000000 Median :3 Median :11
## Mean :0.01339 Mean :0.009375 Mean :3 Mean :11
## 3rd Qu.:0.00000 3rd Qu.:0.000000 3rd Qu.:3 3rd Qu.:11
## Max. :1.00000 Max. :1.000000 Max. :3 Max. :11
##
## Response
## Min. :0.0000
## 1st Qu.:0.0000
## Median :0.0000
## Mean :0.1491
## 3rd Qu.:0.0000
## Max. :1.0000
##
A simple vista ja veiem que hi han algunes dades extremes com el mínim Year_Birth i els màxims Income i NumWebPurchases entre d’altres. Solucionarem aquest problema més endavant.Ara estudiem els valors en blanc i nuls:
# Valors en blanc
colSums(marketing=="")
## ID Year_Birth Education Marital_Status
## 0 0 0 0
## Income Kidhome Teenhome Dt_Customer
## NA 0 0 0
## Recency MntWines MntFruits MntMeatProducts
## 0 0 0 0
## MntFishProducts MntSweetProducts MntGoldProds NumDealsPurchases
## 0 0 0 0
## NumWebPurchases NumCatalogPurchases NumStorePurchases NumWebVisitsMonth
## 0 0 0 0
## AcceptedCmp3 AcceptedCmp4 AcceptedCmp5 AcceptedCmp1
## 0 0 0 0
## AcceptedCmp2 Complain Z_CostContact Z_Revenue
## 0 0 0 0
## Response
## 0
# Valors nuls
colSums(is.na(marketing))
## ID Year_Birth Education Marital_Status
## 0 0 0 0
## Income Kidhome Teenhome Dt_Customer
## 24 0 0 0
## Recency MntWines MntFruits MntMeatProducts
## 0 0 0 0
## MntFishProducts MntSweetProducts MntGoldProds NumDealsPurchases
## 0 0 0 0
## NumWebPurchases NumCatalogPurchases NumStorePurchases NumWebVisitsMonth
## 0 0 0 0
## AcceptedCmp3 AcceptedCmp4 AcceptedCmp5 AcceptedCmp1
## 0 0 0 0
## AcceptedCmp2 Complain Z_CostContact Z_Revenue
## 0 0 0 0
## Response
## 0
Com podem veure no trobem valors en blanc, en canvi si que trobem alguns valors nuls en quant als ingressos dels clients (Income), entenc que això és degut a que és una dada molt sensible i no tots els clients deuen estar disposats a compartir-la. En resum, tot i aquesta petita quantitat de valors nuls el conjunt de dades ja està molt ben netejat. Simplement eliminem les dades amb valors nuls:
no_na_marketing <- na.omit(marketing)
colSums(is.na(no_na_marketing))
## ID Year_Birth Education Marital_Status
## 0 0 0 0
## Income Kidhome Teenhome Dt_Customer
## 0 0 0 0
## Recency MntWines MntFruits MntMeatProducts
## 0 0 0 0
## MntFishProducts MntSweetProducts MntGoldProds NumDealsPurchases
## 0 0 0 0
## NumWebPurchases NumCatalogPurchases NumStorePurchases NumWebVisitsMonth
## 0 0 0 0
## AcceptedCmp3 AcceptedCmp4 AcceptedCmp5 AcceptedCmp1
## 0 0 0 0
## AcceptedCmp2 Complain Z_CostContact Z_Revenue
## 0 0 0 0
## Response
## 0
Un cop hem eliminat les dade NA ens és més fàcil eliminar els outliers, per fer-ho utilitzarem el mètode IQR:
replace_outliers <- function(column) {
if (length(unique(column)) > 2 ) {
column_iqr <- IQR(column, na.rm = TRUE)
upper_bound <- quantile(column, 0.75, na.rm = TRUE) + 1.5 * column_iqr
lower_bound <- quantile(column, 0.25, na.rm = TRUE) - 1.5 * column_iqr
outliers <- column > upper_bound | column < lower_bound
column[outliers] <- median(column, na.rm = TRUE)
}
return(column)
}
numeric_columns <- sapply(no_na_marketing, is.numeric)
no_na_marketing[, numeric_columns] <- apply(no_na_marketing[, numeric_columns], 2, replace_outliers)
summary(no_na_marketing)
## ID Year_Birth Education Marital_Status
## Min. : 0 Min. :1940 Length:2216 Length:2216
## 1st Qu.: 2815 1st Qu.:1959 Class :character Class :character
## Median : 5458 Median :1970 Mode :character Mode :character
## Mean : 5588 Mean :1969
## 3rd Qu.: 8422 3rd Qu.:1977
## Max. :11191 Max. :1996
## Income Kidhome Teenhome Dt_Customer
## Min. : 1730 Min. :0.0000 Min. :0.0000 Length:2216
## 1st Qu.: 35303 1st Qu.:0.0000 1st Qu.:0.0000 Class :character
## Median : 51377 Median :0.0000 Median :0.0000 Mode :character
## Mean : 51633 Mean :0.4418 Mean :0.5054
## 3rd Qu.: 68180 3rd Qu.:1.0000 3rd Qu.:1.0000
## Max. :113734 Max. :2.0000 Max. :2.0000
## Recency MntWines MntFruits MntMeatProducts
## Min. : 0.00 Min. : 0.0 Min. : 0.00 Min. : 0.0
## 1st Qu.:24.00 1st Qu.: 24.0 1st Qu.: 2.00 1st Qu.: 16.0
## Median :49.00 Median : 174.2 Median : 8.00 Median : 68.0
## Mean :49.01 Mean : 286.7 Mean :13.63 Mean :112.9
## 3rd Qu.:74.00 3rd Qu.: 482.0 3rd Qu.:18.00 3rd Qu.:153.0
## Max. :99.00 Max. :1224.0 Max. :79.00 Max. :555.0
## MntFishProducts MntSweetProducts MntGoldProds NumDealsPurchases
## Min. : 0.00 Min. : 0.00 Min. : 0.00 Min. :0.000
## 1st Qu.: 3.00 1st Qu.: 1.00 1st Qu.: 9.00 1st Qu.:1.000
## Median : 12.00 Median : 8.00 Median : 24.25 Median :2.000
## Mean : 21.32 Mean :13.85 Mean : 30.06 Mean :2.065
## 3rd Qu.: 28.00 3rd Qu.:18.00 3rd Qu.: 41.00 3rd Qu.:3.000
## Max. :120.00 Max. :81.00 Max. :126.00 Max. :6.000
## NumWebPurchases NumCatalogPurchases NumStorePurchases NumWebVisitsMonth
## Min. : 0.000 Min. : 0.00 Min. : 0.000 Min. : 0.000
## 1st Qu.: 2.000 1st Qu.: 0.00 1st Qu.: 3.000 1st Qu.: 3.000
## Median : 4.000 Median : 2.00 Median : 5.000 Median : 6.000
## Mean : 4.057 Mean : 2.55 Mean : 5.801 Mean : 5.276
## 3rd Qu.: 6.000 3rd Qu.: 4.00 3rd Qu.: 8.000 3rd Qu.: 7.000
## Max. :11.000 Max. :10.00 Max. :13.000 Max. :13.000
## AcceptedCmp3 AcceptedCmp4 AcceptedCmp5 AcceptedCmp1
## Min. :0.00000 Min. :0.00000 Min. :0.0000 Min. :0.00000
## 1st Qu.:0.00000 1st Qu.:0.00000 1st Qu.:0.0000 1st Qu.:0.00000
## Median :0.00000 Median :0.00000 Median :0.0000 Median :0.00000
## Mean :0.07356 Mean :0.07401 Mean :0.0731 Mean :0.06408
## 3rd Qu.:0.00000 3rd Qu.:0.00000 3rd Qu.:0.0000 3rd Qu.:0.00000
## Max. :1.00000 Max. :1.00000 Max. :1.0000 Max. :1.00000
## AcceptedCmp2 Complain Z_CostContact Z_Revenue
## Min. :0.00000 Min. :0.000000 Min. :3 Min. :11
## 1st Qu.:0.00000 1st Qu.:0.000000 1st Qu.:3 1st Qu.:11
## Median :0.00000 Median :0.000000 Median :3 Median :11
## Mean :0.01354 Mean :0.009477 Mean :3 Mean :11
## 3rd Qu.:0.00000 3rd Qu.:0.000000 3rd Qu.:3 3rd Qu.:11
## Max. :1.00000 Max. :1.000000 Max. :3 Max. :11
## Response
## Min. :0.0000
## 1st Qu.:0.0000
## Median :0.0000
## Mean :0.1503
## 3rd Qu.:0.0000
## Max. :1.0000
Finalment mostrem l’histograma dels atributs més rellevants:
histList<- list()
n = c("Year_Birth", "Income", "Kidhome", "Teenhome", "Recency", "MntWines", "MntFruits", "MntMeatProducts", "MntFishProducts", "MntSweetProducts",
"NumWebPurchases", "NumStorePurchases")
marketingAux= no_na_marketing %>% select(all_of(n))
for(i in 1:ncol(marketingAux)){
col <- names(marketingAux)[i]
ggp <- ggplot(marketingAux, aes_string(x = col)) +
geom_histogram(bins = 30, fill = "cornflowerblue", color = "black")
histList[[i]] <- ggp
}
multiplot(plotlist = histList, cols = 3)
Un cop vistes les gràfiques m’ha sorprès veure que la compra de vi és la més recurrent, seguida d’aprop de la compra de carn i de lluny dels altres productes. Per altre banda veiem com el nombre de dies de l’última compra és bastant variat, les famílies solen tenir entre 0-1 nens o adolescents i el volum de compres està bastant igualat entre la botiga i la web.
A l’apartat anterior ja hem netejat les dades, per tant ja podriem dir que estan preparades per ser analitzades. No obstant com que només tenim 2 variables categòriques discretitzarem variables numèriques més per tenir més varietat. A més ajuntarem les variables Teenhome i Kidhome per crear una variable Children, les vendes totals dels productes i el nombre de compres global.
clean_marketing <- no_na_marketing
clean_marketing$Age <- cut(clean_marketing$Year_Birth, breaks = c(1940, 1958, 1978, 1998),
labels = c(">65", "45-65", "25-45"))
clean_marketing$Social_Class <- cut(clean_marketing$Income, breaks = c(0, 30000, 75000, 200000),
labels = c("Lower", "Middle", "Upper"))
clean_marketing$Children <- clean_marketing$Kidhome + clean_marketing$Teenhome
clean_marketing$MntTotal <- rowSums(clean_marketing[, 10:15], na.rm = TRUE)
clean_marketing$TotalPurchases <- rowSums(clean_marketing[, 16:20], na.rm = TRUE)
A continuació mostrem els histogrames de les noves variables generades:
n = c("Age", "Social_Class")
par(mfrow = c(2, 3))
for (col in n) {
# Create a summary table of the factor levels
class_summary <- table(clean_marketing[[col]])
# Calculate percentages
class_percentages <- prop.table(class_summary) * 100
# Create a pie chart with percentages in labels
pie(class_summary,
labels = paste(names(class_summary), ": ", sprintf("%.1f%%", class_percentages)),
main = paste(col, " Distribution"),
cex = 0.8) # Adjust label font size if needed
}
histList<- list()
Com a conclusió d’aquest anàlisi podem dir que el nostre client òptim és de classe alta ja que gasta el doble que el de classe mitja i accepta la mateixa quantitat de campanyes tot i ser només la meitat de clients.
En quant a la campanya, podem dir que haurà de ser sobre vins i carn, en presencial a la botiga tot i que promocionada també al catàleg i per la web. Aquesta campanya es podrà fer en qualsevol moment ja que no hem aconseguit veure un patró temporal de vendes.